﻿using System;
using System.Drawing;
using System.Drawing.Imaging; 


namespace IBoLT.Cellular
{ 
	/// <summary> 
	/// Cellular texture helper 
	/// </summary> 
	public class CellularTexture
	{
		// Уровень исходной текстуры
		public double lvlOriginal = 0.5;
		// Уровень созданой текстуры
		public double lvlCreate = 0.5;

		public int Seed;

		// Размер генерируемой текстуры
		private int tWidth = 300;
		private int tHeight = 300;

		// Матрица с расстояниями от пикселя до точек
		private float[,] DistanceBuffer;

		// Текстура, на которую накладывается генератор
		private Bitmap bm;

		public Bitmap Texture
		{
			get { return bm; }
			set
			{
				bm = value;
				tWidth = bm.Width;
				tHeight = bm.Height;
				DistanceBuffer = new float[tWidth, tHeight];
			}
		}

		// Количество опорных точек
		private int numberOfPoints = 10;
		// Матрица координат опорных точек
		private Point[] PointArray;

		public int NumberOfPoint
		{
			get { return numberOfPoints; }
			set
			{
				numberOfPoints = System.Math.Max(0, value);
				PointArray = new Point[numberOfPoints];
			}
		}

		public int typeColoring = 0;

		public int[] palette = { 8372160, 16760832, 16711936 };

		private float MinDistance = float.MaxValue;
		private float MaxDistance = float.MinValue;

		public CellularTexture() { }

		public void CalculateDistance()
		{
			MinDistance = float.MaxValue;
			MaxDistance = float.MinValue;

			System.Random Generator = new System.Random(Seed);
			
			for ( int x = 0; x < numberOfPoints; ++x)
			{
				PointArray[x].X = Generator.Next(0, tWidth);
				PointArray[x].Y = Generator.Next(0, tHeight);
			}
			
			for ( int y = 0; y < tHeight; ++y)
			{
				for (int x = 0; x < tWidth; ++x)
				{
					DistanceBuffer[x, y] = DistanceNearestPoint(x, y, PointArray);
					if (DistanceBuffer[x, y] > MaxDistance)
						MaxDistance = DistanceBuffer[x, y];
					else if (DistanceBuffer[x, y] < MinDistance)
						MinDistance = DistanceBuffer[x, y];
				}
			}
		}

		public Bitmap ToBitmap()
		{
			Bitmap ReturnValue = new Bitmap(tWidth, tHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
			BitmapData ImageData = IBoLT.SystemDrawingHelper.LockImage(ReturnValue);
			int ImagePixelSize = IBoLT.SystemDrawingHelper.GetPixelSize(ImageData);
			BitmapData bmData = IBoLT.SystemDrawingHelper.LockImage(bm);
			int bmPixelSize = IBoLT.SystemDrawingHelper.GetPixelSize(bmData);

			for (int x = 0; x < tWidth; ++x) 
			{
				for ( int y = 0; y < tHeight; ++y) 
				{ 
					float Value = GetHeight(x, y, DistanceBuffer, MinDistance, MaxDistance);
					palette[0] = System.Math.Max(System.Math.Min((int)(((palette[0] % 255) / 255 + Value) * 255), 255), 0);
					palette[1] = System.Math.Max(System.Math.Min((int)(((palette[1] % 255) / 255 + Value) * 255), 255), 0);
					palette[2] = System.Math.Max(System.Math.Min((int)(((palette[2] % 255) / 255 + Value) * 255), 255), 0);

					// Узнаю цвет пикселя
					Color colorPixel = SystemDrawingHelper.AddBitmapRGB(
						IBoLT.SystemDrawingHelper.GetPixel(bmData, x, y, bmPixelSize),
						Color.FromArgb(palette[0], palette[1], palette[2]), lvlOriginal, lvlCreate);

					IBoLT.SystemDrawingHelper.SetPixel(ImageData, x, y, colorPixel, ImagePixelSize); 
				} 
			}

			IBoLT.SystemDrawingHelper.UnlockImage(bm, bmData);
			IBoLT.SystemDrawingHelper.UnlockImage(ReturnValue, ImageData); 
			return ReturnValue; 
		} 

		private static float GetHeight(float X, float Y, float[,] DistanceBuffer, float MinDistance, float MaxDistance) 
		{ 
			return (DistanceBuffer[(int)X, (int)Y] - MinDistance) / (MaxDistance - MinDistance); 
		} 

		// Определение расстояния до ближайшей опорной точки по метрике: sqrt(x^2 + y^2)
		private static float DistanceNearestPoint( int x, int y, Point[] PointArray) 
		{ 
			float Lowest = float.MaxValue; 
			
			for ( int z = 0; z < PointArray.Length; ++z) 
			{ 
				float Distance = (float)System.Math.Sqrt(((PointArray[z].X - x) * (PointArray[z].X - x)) + ((PointArray[z].Y - y) * (PointArray[z].Y - y))); 
				if (Distance < Lowest) 
				{ 
					Lowest = Distance; 
				} 
			} 
			
			return Lowest; 
		} 
	} 
} 